#pragma once

#include <iostream>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "log.hpp"

// using func_t = std::function<std::string(const std::string &,const std::string&,uint16_t)>;
typedef std::function<std::string(const std::string &,const std::string&,uint16_t)> func_t;

using std::cout;
using std::endl;
using std::string;

Log log;

uint16_t defaultport = 8080;
string defaultip = "0.0.0.0";
const int size = 1024;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR
};

class UdpServer
{
public:
    UdpServer(const uint16_t &port = defaultport, const string &ip = defaultip)
        : sockfd_(0), port_(port), ip_(ip), isrunning_(false)
    {
    }

    void Init()
    {
        // 1.创建udp socket
        //2.udp 的sockt 是全双工的，允许同时读写
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INET
        if (sockfd_ < 0)
        {
            log(Fatal, "socket create error, socket: %d", sockfd_);
            exit(SOCKET_ERR);
        }
        log(Info, "socket creat success, socket: %d", sockfd_);
        // 2.bind socket
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_); // 需要保证我的端口号是网络字节序列，因为该端口号是要发给对方的
        // local.sin_addr.s_addr = inet_addr(ip_.c_str()); // 1.string -> uint32_t  2.uint32_t 必须是网络序列的
        local.sin_addr.s_addr = htons(INADDR_ANY); // 1.string -> uint32_t  2.uint32_t 必须是网络序列的

        if (bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)))
        {
            log(Fatal, "bind error, error: %d, err string: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }

        log(Info, "bind success, error: %d, err string: %s", errno, strerror(errno));
    }

    void CheckUser(const struct sockaddr_in& client, const string& clientip,uint16_t clientport)
    {
        
        auto iter = online_user_.find(clientip);
        if(iter == online_user_.end())
        {
            online_user_.insert({clientip,client});
            cout<<"["<<clientip<<" : "<<clientport<<"] add to onlin user."<<endl;

        }
    }

    void Broadcast(const string& info,const string& clientip,uint16_t clientport )
    {
        for(const auto& user:online_user_ )
        {
            string message = "[";
            message+=clientip;
            message +=":";
            message +=std::to_string(clientport);
            message+="]# ";
            message+=info;
            socklen_t len = sizeof(user.second);
            sendto(sockfd_, message.c_str(), message.size(), 0, (const sockaddr *)&user.second, len);
        }
    }

    void Run() // 对代码进行分层
    {
        isrunning_ = true;
        char inbuffer[size];

        while (isrunning_)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&client, &len);

            if (n < 0)
            {
                log(Warning, "brecvfromind error, error: %d, err string: %s", errno, strerror(errno));
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port);
            string clientip = inet_ntoa(client.sin_addr);
            CheckUser(client,clientip,clientport);

            string info = inbuffer;
            Broadcast(info,clientip,clientport);
        }
    }

    ~UdpServer()
    {
    }

private:
    int sockfd_;    // 网络文件描述符
    string ip_;     // 任意地址绑定  bind:0
    uint16_t port_; // 表明服务器的端口号
    bool isrunning_;
    std::unordered_map<std::string,struct sockaddr_in> online_user_;
};